<template>
  <div class="h-full w-full flex flex-col relative">
    <!-- 图谱切换滑块 -->
    <div
      class="bg-white rounded-lg shadow-lg border border-gray-200 p-3 w-48 absolute top-10 left-10"
    >
      <div class="text-sm font-medium text-gray-700 mb-2">图谱类型</div>
      <div class="relative">
        <!-- 滑动背景 -->
        <div class="rounded-lg p-1 relative">
          <!-- 滑动指示器 -->
          <div
            class="absolute top-1 left-1 bg-[#514ef2] rounded-md transition-transform duration-300 ease-in-out"
            :style="{
              width: 'calc(100% - 8px)',
              height: '32px',
              transform: `translateY(${activeGraphIndex * 36}px)`,
            }"
          ></div>

          <!-- 图谱选项 - 竖向排列 -->
          <div class="flex flex-col relative z-10 space-y-1">
            <button
              v-for="(graph, index) in graphTypes"
              :key="graph.key"
              @click="switchGraph(graph.key, index)"
              class="w-full px-3 py-1.5 text-sm font-medium rounded-md transition-colors duration-200 relative text-center cursor-pointer"
              :class="
                activeGraph === graph.key ? 'text-white' : 'text-gray-600 hover:text-gray-800'
              "
            >
              {{ graph.name }}
            </button>
          </div>
        </div>
      </div>

      <!-- 当前图谱信息 -->
      <div class="mt-3 text-xs text-gray-500">
        当前: {{ currentGraphInfo.name }}
        <span class="ml-1 text-[#514ef2]">({{ currentGraphInfo.nodeCount }} 节点)</span>
      </div>
    </div>

    <!-- 新增节点 -->
    <div class="absolute bottom-[160px] 2xl:bottom-[310px] left-4 z-10 flex flex-col gap-2">
      <a-button type="primary"  @click="showAddNodeModal"> 新增节点 </a-button>
    </div>

    <!-- 右上角树形控件 -->
    <div
      class="absolute top-4 right-4 z-10 w-80 bg-white rounded-lg shadow-lg border border-gray-200"
    >
      <div class="p-4 border-b border-gray-200">
        <div class="flex items-center justify-between mb-3">
          <h3 class="font-semibold text-gray-800">节点树形结构</h3>
          <div class="flex items-center space-x-2">
            <a-badge :count="allNodesData.length" :number-style="{ backgroundColor: '#52c41a' }" />
            <a-button
              type="text"
              size="small"
              @click="toggleTreePanel"
              :icon="
                h('span', {
                  class: showTreePanel ? 'icon-[mingcute--up-line]' : 'icon-[mingcute--down-line]',
                })
              "
            />
          </div>
        </div>

        <!-- 优化控制面板 -->
        <div class="space-y-2 mb-3">
          <!-- 节点类型快速加载 - 根据当前图谱类型动态显示 -->
          <div class="flex flex-wrap gap-1">
            <a-button
              v-for="nodeType in currentGraphNodeTypes"
              :key="nodeType"
              size="small"
              @click="loadNodesByType(nodeType)"
              :loading="isLoading"
            >
              {{ nodeType }}
            </a-button>
          </div>

          <!-- 分页控制 -->
          <div class="flex items-center space-x-2">
            <a-button size="small" @click="loadNodesPage(1)" :loading="isLoading">
              重新加载
            </a-button>
          </div>
        </div>

        <a-input-search
          v-model:value="treeSearchValue"
          placeholder="搜索节点..."
          size="small"
          @search="searchNodes"
          class="mb-3"
        />
      </div>

      <div v-show="showTreePanel" class="p-4 transition-all duration-300">
        <a-tree
          v-model:checkedKeys="selectedTreeKeys"
          v-model:expandedKeys="expandedTreeKeys"
          :tree-data="filteredTreeData"
          :checkable="true"
          :show-line="true"
          :show-icon="true"
          :selectable="true"
          :height="280"
          @check="onTreeCheck"
          @select="onTreeSelect"
          class="custom-tree"
        >
          <template #title="{ title, nodeType, count, nodeId, properties }">
            <div class="flex items-center justify-between w-full">
              <span class="flex items-center">
                <span
                  class="w-2 h-2 rounded-full mr-2"
                  :style="{ backgroundColor: getNodeTypeColor(nodeType) }"
                ></span>
                <span class="truncate" :title="title">{{ title }}</span>
              </span>
              <div class="flex items-center space-x-1">
                <a-tag v-if="count" :color="getNodeTypeColor(nodeType)">
                  {{ count }}
                </a-tag>
                <!-- 为叶子节点显示节点ID -->
                <span v-if="nodeId && !count" class="text-xs text-gray-400"> #{{ nodeId }} </span>
              </div>
            </div>
          </template>
        </a-tree>
      </div>
    </div>

    <div id="viz" class="w-full flex-1 overflow-hidden"></div>
    <div class="h-[150px] 2xl:h-[300px] w-full flex">
      <div class="flex-1 border-r border-zinc-200 w-full scrollBarY px-4">
        <div class="border-b border-zinc-200 flex justify-between items-center">
          <div>
            <div class="font-bold text-lg">Node details</div>
            <a-tag v-if="curNode?.group" color="#514ef2" class="!my-2">{{ curNode?.group }}</a-tag>
          </div>
          <div class="space-x-2">
            <a-button
              v-if="curNode"
              type="primary"
              danger
              @click="deleteCurrentNode"
              :loading="deletingNode"
            >
              删除节点
            </a-button>
          </div>
        </div>
        <div class="flex justify-between items-center w-full px-4 py-1">
          <span class="w-[20%]">Key</span>
          <span class="flex-1">Value</span>
        </div>
        <div
          class="flex justify-between items-center w-full px-4 border-t border-zinc-200 p-2"
          v-for="(item, key) in curProperties"
          :key="key"
        >
          <span class="w-[20%] font-bold">{{ key }}</span>
          <span class="flex-1">{{ item }}</span>
        </div>
      </div>
      <div class="flex-1 border-r border-zinc-200 px-4 scrollBarY">
        <div>
          <div class="font-bold text-lg">Triples</div>
        </div>
        <div class="flex justify-between items-center w-full px-4 pt-4 font-bold">
          <span class="w-[30%]">源节点</span>
          <span class="w-[25%]">关系</span>
        </div>
        <div
          v-for="(triple, index) in currentTriples"
          :key="index"
          class="flex justify-between items-center w-full px-4 py-2 border-b border-zinc-200 hover:bg-gray-100"
        >
          <span class="w-[30%] text-sm">{{
            Object.values(triple.related_node_properties)[0]
          }}</span>
          <span class="w-[25%] text-sm text-blue-600">{{ triple.type }}</span>
        </div>
      </div>
      <div class="flex-1 px-4">
        <div>
          <div class="font-bold text-lg">Chunks</div>
        </div>
        <div
          class="flex justify-between items-center w-full px-4 pt-4 pb-1 font-bold border-b border-zinc-200"
        >
          <span class="w-[10%]">#</span>
          <span class="w-[15%]">Type</span>
          <span class="flex-1">Source</span>
          <span class="w-[30%]">Created</span>
        </div>
        <div
          v-for="(chunk, index) in currentChunks"
          :key="index"
          class="flex justify-between items-center w-full h-8 px-4 py-1 hover:bg-gray-200 rounded-lg cursor-pointer"
          :class="index % 2 === 0 ? 'bg-black/5' : ''"
          @click="viewChunkDetail(chunk)"
        >
          <span class="w-[10%]">{{ index + 1 }}</span>
          <span class="w-[15%]">{{ chunk.type }}</span>
          <span class="flex-1 truncate">{{ chunk.source }}</span>
          <span class="w-[30%]">{{ chunk.created }}</span>
        </div>

        <!-- 文档详情弹窗 -->
        <a-modal v-model:open="chunkDetailVisible" title="文档详情" width="800px" :footer="null">
          <div v-if="selectedChunk">
            <a-descriptions :column="1" bordered class="mb-4">
              <a-descriptions-item label="文档名称">
                {{ selectedChunk.source }}
              </a-descriptions-item>
              <a-descriptions-item label="文档类型">
                <a-tag>{{ selectedChunk.type }}</a-tag>
              </a-descriptions-item>
              <a-descriptions-item label="创建时间">
                {{ selectedChunk.created }}
              </a-descriptions-item>
              <a-descriptions-item label="文档大小">
                {{ selectedChunk.size }}
              </a-descriptions-item>
            </a-descriptions>

            <div class="flex items-center justify-between mb-3">
              <h4 class="mb-0">文档内容预览</h4>
              <div
                v-if="
                  tempSelectedParagraphIndex !== null &&
                  tempSelectedParagraphIndex !== selectedParagraphIndex
                "
                class="space-x-2"
              >
                <a-button size="small" @click="cancelParagraphSelection">取消</a-button>
                <a-button type="primary" size="small" @click="confirmParagraphSelection"
                  >确认</a-button
                >
              </div>
            </div>

            <div class="max-h-[300px] overflow-y-auto p-4 bg-gray-50 rounded">
              <div v-if="Array.isArray(selectedChunk.content)" class="space-y-3">
                <div
                  v-for="(paragraph, index) in selectedChunk.content"
                  :key="index"
                  class="p-3 bg-white rounded border-l-4 cursor-pointer transition-all duration-200"
                  :class="
                    selectedParagraphIndex === index
                      ? 'border-[#514ef2] bg-[#514ef2]/5 shadow-md'
                      : tempSelectedParagraphIndex === index
                        ? 'border-orange-400 bg-orange-50 shadow-sm'
                        : 'border-[#514ef2]/20 hover:border-[#514ef2]/40 hover:bg-[#514ef2]/2'
                  "
                  @click="selectParagraph(index)"
                >
                  <div class="flex items-center justify-between mb-2">
                    <span
                      class="text-xs font-medium"
                      :class="
                        selectedParagraphIndex === index
                          ? 'text-[#514ef2]'
                          : tempSelectedParagraphIndex === index
                            ? 'text-orange-500'
                            : 'text-gray-500'
                      "
                    >
                      段落 {{ index + 1 }}
                      <span v-if="selectedParagraphIndex === index" class="ml-2 text-xs"
                        >（当前选中）</span
                      >
                      <span
                        v-else-if="tempSelectedParagraphIndex === index"
                        class="ml-2 text-xs text-orange-500"
                        >（待确认）</span
                      >
                    </span>
                    <span class="text-xs text-gray-400">{{ paragraph.length }} 字符</span>
                  </div>
                  <p class="text-sm leading-relaxed text-gray-800">{{ paragraph }}</p>
                </div>
              </div>
              <p v-else class="text-sm leading-relaxed">{{ selectedChunk.content }}</p>
            </div>
          </div>
        </a-modal>
      </div>
    </div>
    <!-- 新增节点弹窗 -->
    <a-modal
      v-model:open="addNodeModalVisible"
      title="新增节点"
      width="600px"
      @ok="handleAddNode"
      :confirm-loading="addingNode"
    >
      <a-form :model="addNodeForm" layout="vertical" ref="addNodeFormRef">
        <a-form-item
          label="实体名称"
          name="entity"
          :rules="[{ required: true, message: '请输入实体名称' }]"
        >
          <a-input v-model:value="addNodeForm.entity" placeholder="请输入实体名称" />
        </a-form-item>

        <a-form-item
          label="关系类型"
          name="relationship"
          :rules="[{ required: true, message: '请输入关系类型' }]"
        >
          <a-select
            v-model:value="addNodeForm.relationship"
            placeholder="请选择或输入关系类型"
            mode="combobox"
          >
            <a-select-option value="表现为">表现为</a-select-option>
            <a-select-option value="根本原因是">根本原因是</a-select-option>
            <a-select-option value="由...导致">由...导致</a-select-option>
          </a-select>
        </a-form-item>

        <a-form-item
          label="目标实体"
          name="entity_source"
          :rules="[{ required: true, message: '请输入目标实体名称' }]"
        >
          <a-input v-model:value="addNodeForm.entity_source" placeholder="请输入目标实体名称" />
        </a-form-item>

        <a-form-item
          label="实体标签"
          name="label"
          :rules="[{ required: true, message: '请选择实体标签' }]"
        >
          <a-select v-model:value="addNodeForm.label" placeholder="请选择实体标签">
            <a-select-option v-for="label in dynamicLabels" :key="label" :value="label">
              {{ label }}
            </a-select-option>
          </a-select>
        </a-form-item>

        <a-form-item
          label="目标实体标签"
          name="label_source"
          :rules="[{ required: true, message: '请选择目标实体标签' }]"
        >
          <a-select v-model:value="addNodeForm.label_source" placeholder="请选择目标实体标签">
            <a-select-option v-for="label in dynamicLabels" :key="label" :value="label">
              {{ label }}
            </a-select-option>
          </a-select>
        </a-form-item>
      </a-form>
    </a-modal>
  </div>
</template>

<script setup>
import { ref, computed, onMounted, h, watch } from 'vue'
import NeoVis from 'neovis.js'
import {
  queryDocumentSegment,
  getNodeInfo,
  deleteNode,
  getTreeData,
  addNode,
  getNeoLabels,
  getGraphData,
} from '@/api/api.js'
import { message } from 'ant-design-vue'

// 现有变量保持不变
let viz
const curNode = ref(null)
const curProperties = ref(null)
const chunkDetailVisible = ref(false)
const selectedChunk = ref(null)
const deletingNode = ref(false)
const selectedParagraphIndex = ref(0)
const tempSelectedParagraphIndex = ref(null)
const chunkParagraphMap = ref(new Map())
const currentTriples = ref([])
const dynamicLabels = ref([])

// 修复当前chunks数据 - 添加模拟数据
const currentChunks = ref([
  {
    type: 'PDF',
    source: '汽车零部件供应链质量管理文档.pdf',
    created: '2025-01-15',
    size: '2.3MB',
    content: [
      '供应链管理是现代企业管理的重要组成部分，它涉及从原材料采购到最终产品交付的整个过程。',
      '在汽车制造业中，零部件供应商的质量管理直接影响到整车的质量和安全性能。',
      '建立完善的供应商评估体系，包括质量体系认证、现场审核、样品测试等多个环节。',
    ],
  },
  {
    type: 'DOCX',
    source: '质量问题处理流程.docx',
    created: '2025-01-10',
    size: '1.8MB',
    content: [
      '质量问题的及时发现和处理是保证产品质量的关键环节。',
      '建立快速响应机制，确保问题在第一时间得到有效处理。',
    ],
  },
])

// 树形控件相关变量 - 动态设置选中状态
const showTreePanel = ref(true)
const selectedTreeKeys = ref([])
const expandedTreeKeys = ref([])
const treeSearchValue = ref('')

// 根据动态标签更新选中状态
const updateSelectedTreeKeys = () => {
  selectedTreeKeys.value = dynamicLabels.value.map((label) => `type-${label}`)
}

// 监听动态标签变化
watch(
  dynamicLabels,
  () => {
    updateSelectedTreeKeys()
  },
  { immediate: true },
)

// 模拟节点类型和颜色映射
const nodeTypeColors = {
  零件名称: '#1890ff',
  故障类别: '#fa541c',
  根本原因: '#7be141',
  故障现象: '#ffff00',
}

// Mock 树形数据
const allNodesData = ref([
  {
    title: '发起人',
    key: 'type-发起人',
    nodeType: '发起人',
    count: 1,
    children: [
      {
        title: null,
        key: 'node-9549',
        nodeType: '发起人',
        nodeId: 9549,
        isLeaf: true,
        properties: {
          姓名: '许桂英',
          部门: '生产部',
        },
      },
    ],
  },
])

// 处理树形数据，为子节点生成合适的标题
const processedTreeData = computed(() => {
  return allNodesData.value.map((typeNode) => ({
    ...typeNode,
    children: typeNode.children?.map((childNode) => ({
      ...childNode,
      title: generateNodeTitle(childNode),
    })),
  }))
})

// 生成节点标题的函数
const generateNodeTitle = (node) => {
  if (node.title) return node.title

  const props = node.properties || {}

  // 根据不同节点类型生成不同的标题
  switch (node.nodeType) {
    case '零部件':
      return props.名称 || props.编号 || `零部件-${node.nodeId}`
    case '供应商':
      return props.名称 || props.代码 || `供应商-${node.nodeId}`
    case '责任人':
      return props.姓名 || props.部门 || `责任人-${node.nodeId}`
    case '发起人':
      return props.姓名 || props.部门 || `发起人-${node.nodeId}`
    case '问题事件':
      return props.qr编号 || props.故障现象 || `问题事件-${node.nodeId}`
    default:
      return props.name || props.名称 || `节点-${node.nodeId}`
  }
}

// 获取节点类型颜色
const getNodeTypeColor = (nodeType) => {
  return nodeTypeColors[nodeType] || '#666666'
}

// 过滤树形数据（搜索功能）
const filteredTreeData = computed(() => {
  const dataToFilter = processedTreeData.value

  if (!treeSearchValue.value) return dataToFilter

  const filterTree = (nodes) => {
    return nodes
      .map((node) => {
        const children = node.children ? filterTree(node.children) : []
        const matchTitle = node.title?.toLowerCase().includes(treeSearchValue.value.toLowerCase())

        // 也搜索子节点的属性
        const matchProperties =
          node.properties &&
          Object.values(node.properties).some((value) =>
            String(value).toLowerCase().includes(treeSearchValue.value.toLowerCase()),
          )

        if (matchTitle || matchProperties || children.length > 0) {
          return { ...node, children }
        }
        return null
      })
      .filter(Boolean)
  }

  return filterTree(dataToFilter)
})

// 图谱切换相关变量
const activeGraph = ref('quality')
const activeGraphIndex = ref(0)

// 图谱类型配置
const graphTypes = ref([
  { key: 'quality', name: '质量', color: '#514ef2' },
  { key: 'regulation', name: '法规', color: '#f56565' },
  { key: 'process', name: '工艺', color: '#38b2ac' },
  { key: 'engineering', name: '工程', color: '#ed8936' },
  { key: 'management', name: '管理', color: '#9f7aea' },
  { key: 'detection', name: '检测', color: '#48bb78' },
])

// 当前图谱信息
const currentGraphInfo = computed(() => {
  const graphConfig = {
    quality: {
      name: '质量图谱',
      nodeCount: 1247,
      nodeTypes: dynamicLabels.value, // 使用动态标签
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
    regulation: {
      name: '法规图谱',
      nodeCount: 856,
      nodeTypes: ['法规条文', '标准规范', '合规要求', '检查项目'],
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
    process: {
      name: '工艺图谱',
      nodeCount: 2134,
      nodeTypes: ['工艺流程', '工艺参数', '设备工具', '操作步骤'],
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
    engineering: {
      name: '工程图谱',
      nodeCount: 1689,
      nodeTypes: ['工程项目', '设计方案', '技术要求', '验收标准'],
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
    management: {
      name: '管理图谱',
      nodeCount: 943,
      nodeTypes: ['管理制度', '组织架构', '岗位职责', '流程规范'],
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
    detection: {
      name: '检测图谱',
      nodeCount: 1456,
      nodeTypes: ['检测方法', '检测设备', '质量指标', '检测标准'],
      cypher: `MATCH (n)-[r]->(m) RETURN n, r, m`,
    },
  }

  return graphConfig[activeGraph.value] || graphConfig.quality
})

// 当前图谱的节点类型
const currentGraphNodeTypes = computed(() => {
  return currentGraphInfo.value.nodeTypes || []
})

// 切换图谱
const switchGraph = async (graphKey, index) => {
  if (activeGraph.value === graphKey) return

  activeGraph.value = graphKey
  activeGraphIndex.value = index

  // 显示加载状态
  isLoading.value = true

  try {
    // 清空当前选中状态
    curNode.value = null
    curProperties.value = null
    currentTriples.value = []
    allNodesData.value = []
    const cypher = currentGraphInfo.value.cypher
    if (graphKey === 'quality') {
      const res_allData = await getTreeData()
      allNodesData.value = res_allData.data
      // 根据图谱类型加载对应的数据

      // await getGraphData({
      //   file_id: '1',
      // })
      if (viz) {
        viz.renderWithCypher(cypher)
      }
    } else if (graphKey === 'process') {
      viz.clearNetwork()
    } else {
      viz.clearNetwork()
    }

    message.success(`已切换到${currentGraphInfo.value.name}`)
  } catch (error) {
    console.error('切换图谱失败:', error)
    message.error('切换图谱失败')
  } finally {
    isLoading.value = false
  }
}

// 树形控件事件处理
const onTreeCheck = (checkedKeys, { checked, node }) => {
  console.log('Tree checked:', checkedKeys, checked, node)

  // 如果是类型节点，处理其所有子节点
  if (node.key.startsWith('type-')) {
    const childNodeIds = node.children?.map((child) => child.nodeId).filter(Boolean) || []
    if (checked) {
      showNodesInGraph(childNodeIds)
    } else {
      hideNodesInGraph(childNodeIds)
    }
  } else if (node.nodeId) {
    // 单个节点
    if (checked) {
      showNodesInGraph([node.nodeId])
    } else {
      hideNodesInGraph([node.nodeId])
    }
  }
}

const onTreeSelect = (selectedKeys, { selected, node }) => {
  console.log('Tree selected:', selectedKeys, selected, node)

  if (selected && node.nodeId) {
    // 在图谱中聚焦到选中的节点
    focusNodeInGraph(node.nodeId)
  }
}

// 图谱交互方法
const showNodesInGraph = (nodeIds) => {
  if (!viz || !viz.network || nodeIds.length === 0) return

  // 限制一次性显示的节点数量
  const maxNodes = 50
  const limitedNodeIds = nodeIds.slice(0, maxNodes)

  if (nodeIds.length > maxNodes) {
    message.warning(`节点数量过多，只显示前${maxNodes}个节点`)
  }

  try {
    const nodes = viz.network.body.data.nodes
    const edges = viz.network.body.data.edges

    // 显示节点
    const nodeUpdates = limitedNodeIds.map((id) => ({
      id: id,
      hidden: false,
    }))
    nodes.update(nodeUpdates)

    // 显示相关的边
    const allEdges = edges.get()
    const edgeUpdates = allEdges
      .filter((edge) => limitedNodeIds.includes(edge.from) || limitedNodeIds.includes(edge.to))
      .map((edge) => ({
        id: edge.id,
        hidden: false,
      }))

    if (edgeUpdates.length > 0) {
      edges.update(edgeUpdates)
    }

    message.success(`已显示 ${limitedNodeIds.length} 个节点`)
  } catch (error) {
    console.log('显示节点失败:', error)
  }
}

const hideNodesInGraph = (nodeIds) => {
  if (!viz || !viz.network) return

  try {
    const nodes = viz.network.body.data.nodes
    const edges = viz.network.body.data.edges

    // 隐藏节点
    const nodeUpdates = nodeIds.map((id) => ({
      id: id,
      hidden: true,
    }))
    nodes.update(nodeUpdates)

    // 隐藏相关的边
    const allEdges = edges.get()
    const edgeUpdates = allEdges
      .filter((edge) => nodeIds.includes(edge.from) || nodeIds.includes(edge.to))
      .map((edge) => ({
        id: edge.id,
        hidden: true,
      }))

    if (edgeUpdates.length > 0) {
      edges.update(edgeUpdates)
    }

    message.success(`已隐藏 ${nodeIds.length} 个节点`)
  } catch (error) {
    console.log('隐藏节点失败:', error)
  }
}

const showAllNodes = () => {
  message.warning('节点数量过多，建议使用分类加载或搜索功能')
  // 不再支持一次性显示所有节点
}

const hideAllNodes = () => {
  if (!viz || !viz.network) return

  try {
    const nodes = viz.network.body.data.nodes
    const edges = viz.network.body.data.edges

    // 隐藏所有节点
    const allNodes = nodes.get()
    const nodeUpdates = allNodes.map((node) => ({
      id: node.id,
      hidden: true,
    }))
    nodes.update(nodeUpdates)

    // 隐藏所有边
    const allEdges = edges.get()
    const edgeUpdates = allEdges.map((edge) => ({
      id: edge.id,
      hidden: true,
    }))
    edges.update(edgeUpdates)

    // 同步树形控件状态 - 全不选
    selectedTreeKeys.value = []

    message.success('已隐藏所有节点')
  } catch (error) {
    console.log('隐藏所有节点失败:', error)
  }
}

const focusNodeInGraph = (nodeId) => {
  if (!viz || !viz.network) return

  try {
    viz.network.focus(nodeId, {
      scale: 1.5,
      animation: {
        duration: 1000,
        easingFunction: 'easeInOutQuad',
      },
    })
    message.success(`已聚焦到节点: ${nodeId}`)
  } catch (error) {
    console.log('聚焦节点失败:', error)
  }
}

// 工具栏方法
const toggleTreePanel = () => {
  showTreePanel.value = !showTreePanel.value
}

const onTreeSearch = (value) => {
  console.log('搜索:', value)
  // 搜索时自动展开匹配的节点
  if (value) {
    const expandKeys = []
    processedTreeData.value.forEach((typeNode) => {
      const hasMatchingChildren = typeNode.children?.some(
        (child) =>
          child.title?.toLowerCase().includes(value.toLowerCase()) ||
          Object.values(child.properties || {}).some((prop) =>
            String(prop).toLowerCase().includes(value.toLowerCase()),
          ),
      )
      if (hasMatchingChildren) {
        expandKeys.push(typeNode.key)
      }
    })
    expandedTreeKeys.value = [...new Set([...expandedTreeKeys.value, ...expandKeys])]
  }
}

// 现有的其他方法保持不变...
// nodeClick, edgeClick, viewChunkDetail 等方法保持不变

// 监听图谱节点点击，同步到树形控件
const nodeClick = async (node) => {
  console.log(node)
  const res = await getNodeInfo({
    node_id: String(node.nodeId),
  })
  currentTriples.value = res.data.direct_relationships
  console.log('🐯 ~ nodeClick ~ res:', res)

  const properties = node?.node?.raw?.properties || {}
  curNode.value = node.node
  curProperties.value = properties

  // 同步选中状态到树形控件（但不影响显示/隐藏状态）
  const nodeKey = `node-${node.nodeId}`
  // 这里不再自动添加到selectedTreeKeys，因为现在用于控制显示/隐藏
}

const edgeClick = (edge) => {
  console.log(edge)
}

const viewChunkDetail = async (chunk) => {
  selectedChunk.value = chunk

  // 从映射中恢复该文档的选中段落，如果没有则默认为0
  const savedIndex = chunkParagraphMap.value.get(chunk.source) || 0
  selectedParagraphIndex.value = savedIndex
  tempSelectedParagraphIndex.value = null

  chunkDetailVisible.value = true
}

// 选择段落
const selectParagraph = (index) => {
  tempSelectedParagraphIndex.value = index
}

// 确认段落选择
const confirmParagraphSelection = () => {
  selectedParagraphIndex.value = tempSelectedParagraphIndex.value

  // 保存到文档段落映射中
  if (selectedChunk.value) {
    chunkParagraphMap.value.set(selectedChunk.value.source, selectedParagraphIndex.value)
  }

  // 关闭弹窗并提示
  chunkDetailVisible.value = false
  message.success(`修改切片成功，当前选中第 ${selectedParagraphIndex.value + 1} 段`)

  // 重置临时选择
  tempSelectedParagraphIndex.value = null
}

// 取消段落选择
const cancelParagraphSelection = () => {
  tempSelectedParagraphIndex.value = null
}



// 删除当前选中的节点
const deleteCurrentNode = async () => {
  if (!curNode.value) return

  try {
    deletingNode.value = true

    // 获取节点ID
    const nodeId = curNode.value.id

    // 调用删除API（如果有的话）
    await deleteNode({
      node_id: nodeId,
    })
    // 重新获取树形数据
    const res_allData = await getTreeData()
    allNodesData.value = res_allData.data

    // 重新初始化图谱
    // reinitializeGraph()
    viz.renderWithCypher(`MATCH (n)-[r]->(m) RETURN n, r, m`)

    // 清空当前选中的节点信息
    curNode.value = null
    curProperties.value = null
    currentTriples.value = []

    message.success('节点删除成功')
  } catch (error) {
    console.error('删除节点失败:', error)
    message.error('删除节点失败')
  } finally {
    deletingNode.value = false
  }
}

// 新增节点相关变量
const addNodeModalVisible = ref(false)
const addingNode = ref(false)
const addNodeFormRef = ref()
const addNodeForm = ref({
  entity: '',
  relationship: '',
  entity_source: '',
  label: '',
  label_source: '',
})

// 显示新增节点弹窗
const showAddNodeModal = () => {
  addNodeForm.value = {
    entity: '',
    relationship: '',
    entity_source: '',
    label: '',
    label_source: '',
  }
  addNodeModalVisible.value = true
}

// 处理新增节点
const handleAddNode = async () => {
  try {
    await addNodeFormRef.value.validate()
    addingNode.value = true

    // 调用新增节点API
    const response = await addNode(addNodeForm.value)

    console.log('新增节点成功:', response)
    message.success('节点新增成功')

    // 重新渲染图谱
    if (viz) {
      viz.renderWithCypher('MATCH (n)-[r]->(m) RETURN n, r, m')
    }

    // 重新获取树形数据
    const res_allData = await getTreeData()
    allNodesData.value = res_allData.data

    // 关闭弹窗
    addNodeModalVisible.value = false
  } catch (error) {
    console.error('新增节点失败:', error)
    message.error('新增节点失败，请检查输入信息')
  } finally {
    addingNode.value = false
  }
}

// 动态生成标签配置
const generateLabelsConfig = () => {
  const labelsConfig = {}
  dynamicLabels.value.forEach((label) => {
    labelsConfig[label] = { label: '名称' }
  })
  return labelsConfig
}

onMounted(async () => {
  // 先获取动态标签
  await fetchDynamicLabels()

  const res_allData = await getTreeData()
  allNodesData.value = res_allData.data

  const config = {
    containerId: 'viz',
    serverUrl: 'http://localhost:7687',
    serverUser: 'neo4j',
    serverPassword: '11111111',
    initialCypher: `
      MATCH (n)-[r]->(m)
      RETURN n,r,m
      limit 10
    `,
    arrows: true,
    labels: generateLabelsConfig(), // 使用动态生成的标签配置
    relationships: {
      表现为: { label: '关系' },
      根本原因是: { label: '关系' },
      '由...导致': { label: '关系' },
    },
    visConfig: {
      interaction: {
        hover: true,
      },
      nodes: {
        shape: 'circle',
        size: 10,
        widthConstraint: { maximum: 80 },
        font: { size: 16, color: '#333', align: 'center', strokeWidth: 0 },
        margin: 10,
      },
      edges: {
        // font: { size: 8, align: 'center', strokeWidth: 0 },
        arrows: {
          to: {
            enabled: true,
            type: 'arrow',
            scaleFactor: 0.5, // 👉 调小箭头大小
          },
        },
        arrowStrikethrough: false,
        // smooth: { enabled: false },
        width: 1,
        color: {
          color: '#999',
          highlight: '#514ef2',
        },
      },
      // 性能优化配置
      physics: {
        enabled: true,
        solver: 'forceAtlas2Based',
        forceAtlas2Based: {
          gravitationalConstant: -50,
          centralGravity: 0.01,
          springLength: 150,
          springConstant: 0.08,
          damping: 1,
          avoidOverlap: 0.5,
        },
        stabilization: {
          iterations: 100,
        },
      },
    },
  }

  viz = new NeoVis(config)
  viz.render()
  viz.registerOnEvent('clickNode', nodeClick)
  viz.registerOnEvent('clickEdge', edgeClick)
  viz.registerOnEvent('completed', setupGraphInteractions)
})

// 分离图谱交互设置
const setupGraphInteractions = () => {
  console.log(viz.network)
  viz.network.addEventListener('doubleClick', function (params) {
    if (params.nodes.length > 0) {
      const nodeId = params.nodes[0]
      console.log('🧩 Clicked Node ID:', nodeId)
      loadNodeNeighbors(nodeId)
    }
  })
}

// 优化邻居节点加载
const loadedNodeIds = new Set()
const loadNodeNeighbors = (neo4jId) => {
  if (loadedNodeIds.has(neo4jId)) {
    console.log(`⚠ 节点 ${neo4jId} 已加载过邻居，跳过`)
    return
  }
  loadedNodeIds.add(neo4jId)

  const query = `
    MATCH (n) WHERE id(n) = ${neo4jId}
    MATCH (n)-[r]-(m)
    RETURN DISTINCT n, r, m
    LIMIT 10
  `
  viz.updateWithCypher(query)
}

// 添加分页加载功能
const currentPage = ref(1)
const pageSize = ref(100)
const totalNodes = ref(0)
const isLoading = ref(false)

// 分页加载节点
const loadNodesPage = async (page = 1, size = 100) => {
  if (isLoading.value) return

  isLoading.value = true
  try {
    const offset = (page - 1) * size
    const query = `
      MATCH (n)-[r]->(m)
      RETURN n, r, m
      SKIP ${offset}
      LIMIT ${size}
    `

    if (page === 1) {
      viz.renderWithCypher(query)
    } else {
      viz.updateWithCypher(query)
    }

    currentPage.value = page
    message.success(`已加载第 ${page} 页数据`)
  } catch (error) {
    console.error('加载数据失败:', error)
    message.error('加载数据失败')
  } finally {
    isLoading.value = false
  }
}

// 按节点类型加载
const loadNodesByType = async (nodeType) => {
  if (isLoading.value) return

  isLoading.value = true
  try {
    const query = `
      MATCH (n:${nodeType})-[r]-(m)
      RETURN n, r, m
    `
    viz.renderWithCypher(query)
    message.success(`已加载 ${nodeType} 类型节点`)
  } catch (error) {
    console.error('加载节点类型失败:', error)
    message.error('加载节点类型失败')
  } finally {
    isLoading.value = false
  }
}

// 搜索节点
const searchNodes = async (searchTerm) => {
  if (!searchTerm.trim()) return

  isLoading.value = true
  try {
    const query = `
      MATCH (n)-[r]-(m)
      WHERE n.名称 CONTAINS '${searchTerm}'
         OR n.姓名 CONTAINS '${searchTerm}'
         OR m.名称 CONTAINS '${searchTerm}'
         OR m.姓名 CONTAINS '${searchTerm}'
      RETURN n, r, m
      LIMIT 100
    `
    viz.renderWithCypher(query)
    message.success(`搜索到相关节点`)
  } catch (error) {
    console.error('搜索失败:', error)
    message.error('搜索失败')
  } finally {
    isLoading.value = false
  }
}

const mergeNode = () => {}

// 获取动态标签
const fetchDynamicLabels = async () => {
  try {
    const res = await getNeoLabels()
    if (res.code === 200) {
      dynamicLabels.value = res.data
      // 动态更新节点类型颜色映射
      updateNodeTypeColors()
    }
  } catch (error) {
    console.error('获取标签失败:', error)
  }
}

// 动态更新节点类型颜色映射
const updateNodeTypeColors = () => {
  const colors = ['#ffff00', '#fa541c', '#7be141', '#1890ff']
  dynamicLabels.value.forEach((label, index) => {
    nodeTypeColors[label] = colors[index % colors.length]
  })
}
</script>

<style lang="scss" scoped>
.custom-tree {
  :deep(.ant-tree-node-content-wrapper) {
    width: 100%;

    &:hover {
      background-color: #f5f5f5;
    }
  }

  :deep(.ant-tree-title) {
    width: 100%;
  }

  :deep(.ant-tree-checkbox) {
    margin-right: 8px;
  }
}

// 树形面板样式
.tree-panel {
  backdrop-filter: blur(10px);
  background-color: rgba(255, 255, 255, 0.95);
}
</style>
